package com.csun.cmny.provider.service.impl;

import com.csun.cmny.base.dto.LoginAuthDto;
import com.csun.cmny.base.enums.ErrorCodeEnum;
import com.csun.cmny.core.support.BaseService;
import com.csun.cmny.provider.dao.OmcReturnFeeConfigMapper;
import com.csun.cmny.provider.model.domain.OmcPaymentCycle;
import com.csun.cmny.provider.model.domain.OmcReturnFeeConfig;
import com.csun.cmny.provider.model.dto.check_in.CheckInDaysDto;
import com.csun.cmny.provider.model.dto.old_leave.OldLeaveDaysDto;
import com.csun.cmny.provider.model.dto.old_out.OldOutDaysDto;
import com.csun.cmny.provider.model.dto.revise.ReviseDaysDto;
import com.csun.cmny.provider.model.exceptions.UacBizException;
import com.csun.cmny.provider.model.vo.check_in.CheckInDaysVo;
import com.csun.cmny.provider.model.vo.cycle.CycleDaysVo;
import com.csun.cmny.provider.model.vo.old_leave.OldLeaveDaysVo;
import com.csun.cmny.provider.model.vo.old_out.OldOutDaysVo;
import com.csun.cmny.provider.model.vo.revise.ReviseDaysVo;
import com.csun.cmny.provider.service.OmcPaymentCycleService;
import com.csun.cmny.provider.service.OmcReturnFeeConfigService;
import com.csun.cmny.provider.utils.CronUtil;
import com.csun.cmny.util.DateUtil;
import com.csun.cmny.util.PublicUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.quartz.TriggerUtils;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
@Transactional
public class OmcReturnFeeConfigServiceImpl extends BaseService<OmcReturnFeeConfig>
        implements OmcReturnFeeConfigService {

    @Resource
    private OmcReturnFeeConfigMapper omcReturnFeeConfigMapper;
    @Resource
    private OmcPaymentCycleService omcPaymentCycleService;

    @Override
    public Integer saveReturnFeeConfig(OmcReturnFeeConfig omcReturnFeeConfig, LoginAuthDto loginAuthDto) {

        omcReturnFeeConfig.setUpdateInfo(loginAuthDto);

        //校验同一个机构中只有一条配置数据
        //先查询该机构是否有该项配置
        OmcReturnFeeConfig returnFeeConfigQuery = new OmcReturnFeeConfig();
        returnFeeConfigQuery.setGroupId(omcReturnFeeConfig.getGroupId());
        returnFeeConfigQuery = selectOne(returnFeeConfigQuery);

        //如果存在则修改
        if(returnFeeConfigQuery != null){
            omcReturnFeeConfig.setId(returnFeeConfigQuery.getId());
            return omcReturnFeeConfigMapper.updateByPrimaryKeySelective(omcReturnFeeConfig);
            //不存在则插入
        }else{
            return omcReturnFeeConfigMapper.insertSelective(omcReturnFeeConfig);
        }
    }

    @Override
    public OmcReturnFeeConfig selectByGroupId(Long groupId) {
        OmcReturnFeeConfig query = new OmcReturnFeeConfig();
        query.setGroupId(groupId);
        return selectOne(query);
    }

    /**
     * 根据入院日期计算入住月剩余缴纳天数
     *
     * @param checkInDaysDto the check in days dto
     * @return the check in days vo
     */
    @Override
    public CheckInDaysVo queryCheckInDaysVo(CheckInDaysDto checkInDaysDto) {

        CheckInDaysVo checkInDaysVo = new CheckInDaysVo();
        Date checkInDate = checkInDaysDto.getCheckInDate();
        Long groupId = checkInDaysDto.getGroupId();

        CycleDaysVo cycleDaysVo = queryCycleDaysVo(checkInDate, groupId);
        BeanUtils.copyProperties(cycleDaysVo, checkInDaysVo);

        // 节点天数 = 入院日期 - 周期开始日期
        Date last_date = cycleDaysVo.getLastDate();
        int cycleMaxDays = cycleDaysVo.getCycleMaxDays();
        int cycleTotalDays = cycleDaysVo.getCycleTotalDays();
        int days = com.csun.cmny.provider.utils.DateUtil.differentDays(last_date, checkInDate);
        // 入住月缴费天数 = 本周期最大天数 - 节点天数
        days = cycleMaxDays - days;
        if (days < 0) { // 入住天数小于0时，入住天数为0天
            days = 0;
        }
        if (days > cycleTotalDays) {    // 入住天数大于周期总天数时，入住天数为周期总天数
            days = cycleTotalDays;
        }
        //
        checkInDaysVo.setPayDays(days);
        // 入住月缴费剩余周期数
        checkInDaysVo.setPayPercent(new BigDecimal((float) days / cycleTotalDays)
                .setScale(2, BigDecimal.ROUND_HALF_UP));

        return checkInDaysVo;
    }

    /**
     * 根据日期和机构Id查询周期缴费配置信息
     *
     * @param date    the date
     * @param groupId the group id
     * @return the cycle days vo
     */
    @Override
    public CycleDaysVo queryCycleDaysVo(Date date, Long groupId) {

        CycleDaysVo cycleDaysVo = new CycleDaysVo();

        OmcReturnFeeConfig omcReturnFeeConfig = selectByGroupId(groupId);
        if (PublicUtil.isEmpty(omcReturnFeeConfig)) {
            throw new UacBizException(ErrorCodeEnum.OMC10038031);
        }
        cycleDaysVo.setIsZero(omcReturnFeeConfig.getIsZero());
        cycleDaysVo.setReturnFeeDays(omcReturnFeeConfig.getReturnFeeDays());
        cycleDaysVo.setTakeOffDays(omcReturnFeeConfig.getTakeOffDays());

        // 查询周期账单cronExpression
        String cronExpression;

        OmcPaymentCycle omcPaymentCycle = new OmcPaymentCycle();
        omcPaymentCycle.setGroupId(groupId);
        omcPaymentCycle = omcPaymentCycleService.selectOne(omcPaymentCycle);
        if (PublicUtil.isEmpty(omcPaymentCycle)) {
            throw new UacBizException(ErrorCodeEnum.OMC10040001);
        }

        cronExpression = omcPaymentCycle.getPaymentRecordGenerateConfig();
        if (PublicUtil.isEmpty(cronExpression)) {
            throw new UacBizException(ErrorCodeEnum.OMC10040001);
        }

        List<Date> dateList = CronUtil.getNextExecTimeOfMonthWithDate(cronExpression, date);
        // 上一个账单日
        Date last_date = dateList.get(0);
        cycleDaysVo.setLastDate(last_date);

        // 下一个账单日
        Date next_date = dateList.get(1);
        cycleDaysVo.setNextDate(next_date);

        // 周期总天数
        int cycleTotalDays = omcReturnFeeConfig.getCycleTotalDays();
        // 周期最大天数
        int cycleMaxDays = omcReturnFeeConfig.getCycleMaxDays();

        if (cycleTotalDays == 0) { // 按照实际天数计算
            // cycleTotalDays = DateUtil.getDaysOfMonth(date);
            /**
             * @date    2019-11-19
             * @author  chisj
             * @desc    根据账单日获取当月天天数 修复bug #3996
             */
            cycleTotalDays = DateUtil.getDaysOfMonth(last_date);
        }
        cycleDaysVo.setCycleTotalDays(cycleTotalDays);

        if (cycleMaxDays == 0) { // 按照实际天数计算
            // cycleMaxDays = DateUtil.getDaysOfMonth(date);

            /**
             * @date    2019-11-19
             * @author  chisj
             * @desc    根据账单日获取当月天天数 修复bug #3996
             */
            cycleMaxDays = DateUtil.getDaysOfMonth(last_date);
        }
        cycleDaysVo.setCycleMaxDays(cycleMaxDays);

        return cycleDaysVo;
    }

    /**
     * 根据调整日期计算补缴天数
     *
     * @param reviseDaysDto the revise days dto
     * @return the revise days vo
     */
    @Override
    public ReviseDaysVo queryReviseDaysVo(ReviseDaysDto reviseDaysDto) {

        ReviseDaysVo reviseDaysVo = new ReviseDaysVo();
        Date reviseDate = reviseDaysDto.getReviseDate();
        Date checkInDate = reviseDaysDto.getCheckInDate();
        Long groupId = reviseDaysDto.getGroupId();

        CycleDaysVo cycleDaysVo;
        BigDecimal cyclePercent = new BigDecimal(1); // 默认是一个周期

        cycleDaysVo = queryCycleDaysVo(reviseDate, groupId);
        Date last_date = cycleDaysVo.getLastDate();
        if (PublicUtil.isNotEmpty(checkInDate) &&
                checkInDate.compareTo(last_date) >= 0) {    // 入住日期在调房调级周期中
            CheckInDaysDto checkInDaysDto = new CheckInDaysDto();
            checkInDaysDto.setCheckInDate(checkInDate);
            checkInDaysDto.setGroupId(groupId);
            CheckInDaysVo checkInDaysVo = queryCheckInDaysVo(checkInDaysDto);

            cyclePercent = checkInDaysVo.getPayPercent();
            //BeanUtils.copyProperties(checkInDaysVo, cycleDaysVo);
            // last_date = checkInDate;    // 上一个账单日就是入住日期
        }

        if (reviseDate.compareTo(last_date) < 0) {  // 不能早于上一个账单日
            throw new UacBizException(ErrorCodeEnum.OMC10038032, last_date);
        }
        reviseDaysVo.setLastDate(last_date);

        Date next_date = cycleDaysVo.getNextDate();
        if (reviseDate.compareTo(next_date) > 0) {  // 不能早于上一个账单日
            throw new UacBizException(ErrorCodeEnum.OMC10038033);
        }
        reviseDaysVo.setNextDate(next_date);

        /**
         * @date    2019-10-14
         * @author  chisj
         * @desc    费用计算调整
         *
         * 节点天数 = (调整日期 - 本周期开始日期) +１;
         * // 加一的原因是调房调级当天费用按调整前计算
         * 补缴周期数 = 本周期最大天数 - 节点天数 / 本周期总天数
         *
         **/
        // 节点天数计算 = (调整日期（reviseDate） - 本周期开始日期（上一个账单日last_date）) + 1
        int days = com.csun.cmny.provider.utils.DateUtil.differentDays(last_date, reviseDate) + 1;
        // 补缴天数 = 本周期最大天数 - 节点天数
        int cycleMaxDays = cycleDaysVo.getCycleMaxDays();
        int cycleTotalDays = cycleDaysVo.getCycleTotalDays();
        reviseDaysVo.setCycleMaxDays(cycleMaxDays);
        reviseDaysVo.setCycleTotalDays(cycleTotalDays);
        days = cycleMaxDays - days;
        if (days < 0) {
            days = 0;
        }
        if (days > cycleMaxDays) {
            days = cycleMaxDays;
        }
        reviseDaysVo.setPayDays(days);
        BigDecimal payPercent =  new BigDecimal((float) days / cycleTotalDays)
                .setScale(2, BigDecimal.ROUND_HALF_UP);
        reviseDaysVo.setPayPercent(payPercent);

        // 调整日期的周期消费周期数 (本周期预缴周期数-调整补缴周期数)
        reviseDaysVo.setUsedPercent(cyclePercent.subtract(payPercent));
        return reviseDaysVo;
    }


    @Test
    public void main() {

        String cronExpression = "00 00 02 01 1-12 ? *";

        Date reviseDate = DateUtil.stringToDate("2019-10-31");

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(reviseDate);
        calendar.add(Calendar.MONTH, -1);
        Date from = calendar.getTime();
        calendar.add(Calendar.MONTH, 3);
        Date to = calendar.getTime();

                CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
        try {
            cronTriggerImpl.setCronExpression(cronExpression);
        } catch (Exception e) {
            e.printStackTrace();
        }
        List<Date> dateList = TriggerUtils.computeFireTimesBetween(cronTriggerImpl, null, from, to);

        String str_date = DateUtil.dateToStr(reviseDate);
        Date last_date = new Date();
        Date next_date = new Date();
        for (int i = 0; i < dateList.size() - 1; i++) {
            last_date = dateList.get(i);
            next_date = dateList.get(i + 1);
            String l = DateUtil.dateToStr(last_date);
            String n = DateUtil.dateToStr(next_date);
            if (str_date.compareTo(l) >=0 && str_date.compareTo(n) < 0) {
                break;
            }
        }

        System.out.println("dateList ==> " + dateList);

        System.out.println("last_date ==> " + last_date);
        System.out.println("next_date ==> " + next_date);
//        Date last_date = CronUtil.getLastTriggerTime(cronExpression);
//        Date next_date = CronUtil.getNextTriggerTime(cronExpression);
//
//        System.out.println("last_date ==> " + last_date);
//        System.out.println("next_date ==> " + next_date);

        //Date reviseDate = DateUtil.stringToDate("2019-09-30");

        //int days = com.csun.cmny.provider.utils.DateUtil.differentDays(reviseDate, next_date);
        // System.out.println("days ==> " + days);
    }

    /**
     * 根据出院日期计算退费天数 （补缴天数）
     *
     * @param oldOutDaysDto the old out days dto
     * @return the old out days vo
     */
    @Override
    public OldOutDaysVo queryOldOutDaysVo(OldOutDaysDto oldOutDaysDto) {

        OldOutDaysVo oldOutDaysVo = new OldOutDaysVo();

        Long groupId = oldOutDaysDto.getGroupId();
        Date outDate = oldOutDaysDto.getOutDate();
        Date checkInDate = oldOutDaysDto.getInDate();

        CycleDaysVo cycleDaysVo;
        BigDecimal cyclePercent = new BigDecimal(1); // 默认是一个周期

        cycleDaysVo = queryCycleDaysVo(outDate, groupId);
        Date last_date = cycleDaysVo.getLastDate();
        if (checkInDate.compareTo(last_date) >= 0) {    // 入住日期在出院周期中
            CheckInDaysDto checkInDaysDto = new CheckInDaysDto();
            checkInDaysDto.setCheckInDate(checkInDate);
            checkInDaysDto.setGroupId(groupId);
            CheckInDaysVo checkInDaysVo = queryCheckInDaysVo(checkInDaysDto);
            // cycleDaysVo = new CycleDaysVo();
            // BeanUtils.copyProperties(checkInDaysVo, cycleDaysVo);
            cyclePercent = checkInDaysVo.getPayPercent();
            // last_date = checkInDate;    // 上一个账单日就是入住日期
        }

        if (outDate.compareTo(last_date) < 0) {  // 不能早于上一个账单日
            throw new UacBizException(ErrorCodeEnum.OMC10038032, last_date);
        }
        oldOutDaysVo.setNextDate(last_date);

        Date next_date = cycleDaysVo.getNextDate();
        if (outDate.compareTo(next_date) > 0) {  // 不能早于上一个账单日
            throw new UacBizException(ErrorCodeEnum.OMC10038033);
        }
        oldOutDaysVo.setNextDate(next_date);

        /**
         * @date    2019-10-14
         * @author  chisj
         * @desc    用新的计算公式
         */
        // 两个日期的实际天数
        // int days = com.csun.cmny.provider.utils.DateUtil.differentDays(outDate, next_date);

        // 补缴日期当前不计算天数
        /**
         *  需要扣一天 如 账单是2019年xx月01日 00:00:00 ,
         *  下一次账单日为2019年10月01日 00:00:00
         *  调整日期为2019年09月30日，但该天不计算补缴天数
         */

        /**
         * @date    2019-10-14
         * @author  chisj
         * @desc    新的出院退费周期数计算公式
         *
         * 节点天数 = 出院日期 - 本周期开始日期
         *
         * 出院退费周期数 = (本周期最大天数 - 节点天数) / 本周期总天数
         *
         */
        // 节点天数 = 出院日期（outDate） - 本周期开始日期（last_date）
        int days = com.csun.cmny.provider.utils.DateUtil.differentDays(last_date, outDate);
        // 退费天数 = 本周期最大天数 - 节点天数
        int cycleMaxDays = cycleDaysVo.getCycleMaxDays();
        int cycleTotalDays = cycleDaysVo.getCycleTotalDays();
        oldOutDaysVo.setCycleMaxDays(cycleMaxDays);
        oldOutDaysVo.setCycleTotalDays(cycleTotalDays);
        days = cycleMaxDays - days;
        if (days < 0) {
            days = 0;
        }
        if (days > cycleMaxDays) {
            days = cycleMaxDays;
        }
        oldOutDaysVo.setReturnDays(days);
        BigDecimal returnPercent = new BigDecimal((float) days / cycleTotalDays)
                .setScale(2, BigDecimal.ROUND_HALF_UP);
        oldOutDaysVo.setReturnPercent(returnPercent);

        // 调整日期的周期消费周期数 (本周期预缴周期数-调整补缴周期数)
        oldOutDaysVo.setUsedPercent(cyclePercent.subtract(returnPercent));

        return oldOutDaysVo;
    }

    @Override
    public OldLeaveDaysVo queryOldLeaveDaysVo(OldLeaveDaysDto oldLeaveDaysDto) {

        OldLeaveDaysVo oldLeaveDaysVo = new OldLeaveDaysVo();

        Long groupId = oldLeaveDaysDto.getGroupId();
        Date startDate = oldLeaveDaysDto.getStartDate();
        Date endDate = oldLeaveDaysDto.getEndDate();
        Date checkInDate = oldLeaveDaysDto.getInDate();
        oldLeaveDaysVo.setStartDate(startDate);
        oldLeaveDaysVo.setEndDate(endDate);

        CycleDaysVo cycleDaysVo = queryCycleDaysVo(startDate, groupId);
        BeanUtils.copyProperties(cycleDaysVo, oldLeaveDaysVo);
        log.info("请假退费配置 ==》{}", cycleDaysVo.toString());
        Date last_date = cycleDaysVo.getLastDate();
        if (checkInDate.compareTo(last_date) >= 0) {    // 入住日期在出院周期中
            last_date = checkInDate;
        }
        oldLeaveDaysVo.setLastDate(last_date);
        // 请假天数 = 请假结束日期 - 请假开始日期
        int leaveDays = com.csun.cmny.provider.utils.DateUtil.differentDays(startDate, endDate);
        oldLeaveDaysVo.setLeaveDays(leaveDays);
        // 实际请假天数 = 单次请假天数 = 0 ? (changeOneFlag-isZero ? 1 : 0) : 单次请假天数
        Integer realLeaveDays = (leaveDays == 0) ? oldLeaveDaysVo.getIsZero() : leaveDays;
        oldLeaveDaysVo.setRealLeaveDays(realLeaveDays);
        // 请假退费天数 = 实际请假天数 > D1 ? 实际请假天数 - D2 : 0
        int returnDays = 0;
        if (realLeaveDays >= (int) oldLeaveDaysVo.getReturnFeeDays()) {
            returnDays = realLeaveDays - oldLeaveDaysVo.getTakeOffDays();
            if (realLeaveDays < 0) {
                returnDays = 0;
            }
        } else {
            returnDays = 0;
        }
        oldLeaveDaysVo.setReturnDays(returnDays);

        int cycleMaxDays = cycleDaysVo.getCycleMaxDays();
        int cycleTotalDays = cycleDaysVo.getCycleTotalDays();

        BigDecimal dt = new BigDecimal((float) returnDays / cycleTotalDays)
                .setScale(2, BigDecimal.ROUND_HALF_UP);
        oldLeaveDaysVo.setDt(dt);

        Integer endDays = com.csun.cmny.provider.utils.DateUtil.differentDays(last_date, endDate);
        oldLeaveDaysVo.setEndDays(endDays);

        BigDecimal de = new BigDecimal((float) endDays / cycleTotalDays)
                .setScale(2, BigDecimal.ROUND_HALF_UP);
        oldLeaveDaysVo.setDe(de);

        BigDecimal ds = de.subtract(dt);
        oldLeaveDaysVo.setDs(ds);

        return oldLeaveDaysVo;
    }

    public boolean checkGroupId(OmcReturnFeeConfig omcReturnFeeConfig){
        Example example = new Example(OmcReturnFeeConfig.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("groupId",omcReturnFeeConfig.getGroupId());
        if(omcReturnFeeConfig.getId() != null){
            criteria.andNotEqualTo("id",omcReturnFeeConfig.getId());
        }
        int ret = selectCountByExample(example);
        return ret<1;
    }
}
